import WebpackLicense from '@components/WebpackLicense';
import { Stability } from '../../../components/ApiMeta';

<WebpackLicense from="https://webpack.docschina.org/configuration/externals/" />

# Externals

外部依赖：该选项提供了「从输出的 bundle 中排除依赖」的方法。相反，所创建的 bundle 依赖于那些存在于用户环境（consumer's environment）中的依赖。此功能通常对**库开发人员**来说是最有用的，然而也会有各种各样的应用程序用到它。

## externals

- **类型：** `string | object | function | RegExp | Array<string | object | function | RegExp>`

**防止**将某些 `import` 的包（package）**打包**到 bundle 中，而是在运行时（runtime）再去从外部获取这些*扩展依赖*（external dependencies）。

例如，从 CDN 引入 [jQuery](https://jquery.com/)，而不是把它打包：

```html title="index.html"
<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous"
></script>
```

```js title="rspack.config.mjs"
export default {
  //...
  externals: {
    jquery: 'jquery',
  },
};
```

这样就剥离了那些不需要改动的依赖模块，换句话，下面展示的代码还可以正常运行：

```js
import $ from 'jquery';

$('.my-element').animate(/* ... */);
```

在上面 Rspack 配置中，`externals` 下指定的属性名称 `jquery` 表示 `import $ from 'jquery'` 中的模块 `jquery` 应该从打包产物中排除。为了替换这个模块，`jQuery` 值将用于检索全局 `jQuery` 变量，因为默认的外部库类型是 `var`，请参阅 [externalsType](#externalstype)。

虽然我们在上面展示了一个使用外部全局变量的示例，但实际上可以以以下任何形式使用外部变量：全局变量、CommonJS、AMD、ES2015 模块，在 [externalsType](#externalstype) 中查看更多信息。

### 字符串

根据 [externalsType](#externalstype)，这可能是全局变量的名称（参见 [`'global'`](#externalstypeglobal)、[`'this'`](#externalstypethis)、[`'var '`](#externalstypevar)、[`'window'`](#externalstypewindow)）或模块的名称（参见 `amd`、[`commonjs`](#externalstypecommonjs)、[`module`](#externalstypemodule)、`umd`）。

如果你只定义一个 external，也可以使用快捷语法：

```js title="rspack.config.mjs"
export default {
  //...
  externals: 'jquery',
};
```

等价于

```js title="rspack.config.mjs"
export default {
  //...
  externals: {
    jquery: 'jquery',
  },
};
```

你可以使用 `${externalsType} ${libraryName}` 语法来指定[外部库类型](#externalstype)。它将覆盖 [externalsType](#externalstype) 选项中指定的默认外部库类型。

例如，如果外部库是一个 [CommonJS 模块](#externalstypecommonjs)，你可以指定

```js title="rspack.config.mjs"
export default {
  //...
  externals: {
    jquery: 'commonjs jquery',
  },
};
```

### 字符串数组

```js title="rspack.config.mjs"
export default {
  //...
  externals: {
    subtract: ['./math', 'subtract'],
  },
};
```

`subtract: ['./math', 'subtract']` 允许你选择一个模块的一部分，其中 `./math` 是模块名，你的打包只需要包含 `subtract` 变量下的子集。

当 `externalsType` 为 `commonjs` 时，这个例子会编译为 `require('./math').subtract`；当 `externalsType` 为 `window` 时，这个例子会编译为 `window["./math"]["subtract"]`。

与[字符串语法](#字符串)类似，你可以在数组的第一个元素中使用 `${externalsType} ${libraryName}` 语法指定外部库类型，例如：

```js title="rspack.config.mjs"
export default {
  //...
  externals: {
    subtract: ['commonjs ./math', 'subtract'],
  },
};
```

### 对象

:::warning
带有 `{ root, commonjs, commonjs2, amd, ... }` 的对象只允许用于 [`libraryTarget: 'umd'`](/config/output#outputlibrarytarget) 和 [`externalsType: 'umd'`](#externalstype)。其他库的 target 不允许这样做。
:::

```js title="rspack.config.mjs"
export default {
  externals: {
    // 在 `libraryTarget: 'umd'` 和 `externalsType: 'umd'` 时，需严格遵循以下格式：
    lodash: {
      root: '_', // 指向全局变量
      commonjs: 'lodash',
      commonjs2: 'lodash',
      amd: 'lodash',
    },
  },
};
```

此语法用于描述外部 library 所有可用的访问方式。这里 `lodash` 这个外部库可以在 AMD 和 CommonJS 模块系统中通过 `lodash` 访问，但在全局变量形式下用 `_` 访问。`subtract` 可以通过全局 `math` 对象下的属性 `subtract` 访问（例如 `window['math']['subtract']`）。

### 函数

- **类型：**
  - `function ({ context, request, contextInfo, getResolve }, callback)`
  - `function ({ context, request, contextInfo, getResolve }) => promise`

如果你想要自定义外部化的行为，可以使用函数。例如使用 [webpack-node-externals](https://www.npmjs.com/package/webpack-node-externals) 排除所有来自 `node_modules` 目录的模块，并提供了选项来允许列出白名单中的包。

函数接收两个入参：

- `ctx` (`object`): 包含文件的详细信息的对象。
  - `ctx.context` (`string`): 包含 import 的文件的目录。
  - `ctx.request` (`string`): 被请求的 import 路径。
  - `ctx.contextInfo` (`object`): 包含有关发起者的信息（例如 layer 和编译器）。
  - `ctx.getResolve`: 获取一个带有当前解析器选项的解析函数。
- `callback` (`function (err, result, type)`): 用来指示模块如何被外部化的回调函数。

回调函数接收三个参数：

- `err` (`Error`): 用于标识外部化导入时是否出现错误。如果有错误，这应该是唯一使用的参数。
- `result` (`string | string[] | object`): 使用其他外部格式描述外部模块（[字符串](#字符串)，[字符串数组](#字符串数组)或[对象](#对象) ）。
- `type` (`string`): 指示 [externalsType](#externalstype) 的可选参数（如果还没有在 `result` 参数中指出的话）。

例如，要外部化所有导入路径与正则表达式匹配的导入，可以这样做：

```js title="rspack.config.mjs"
export default {
  //...
  externals: [
    function ({ context, request }, callback) {
      if (/^yourregex$/.test(request)) {
        // 将请求路径外部化到 CommonJS 模块
        return callback(null, 'commonjs ' + request);
      }

      // 跳过外部化导入
      callback();
    },
  ],
};
```

其他使用不同模块格式的例子：

```js title="rspack.config.mjs"
export default {
  externals: [
    function (ctx, callback) {
      // 外部化为 `@scope/library` 的 `commonjs2` 模块
      callback(null, '@scope/library', 'commonjs2');
    },
  ],
};
```

```js title="rspack.config.mjs"
export default {
  externals: [
    function (ctx, callback) {
      // 外部化名为 `nameOfGlobal` 的全局变量
      callback(null, 'nameOfGlobal');
    },
  ],
};
```

```js title="rspack.config.mjs"
export default {
  externals: [
    function (ctx, callback) {
      // 外部化为 `@scope/library` 模块中的具名导出
      callback(null, ['@scope/library', 'namedexport'], 'commonjs');
    },
  ],
};
```

```js title="rspack.config.mjs"
export default {
  externals: [
    function (ctx, callback) {
      // 外部化为 UMD 模块
      callback(null, {
        root: 'componentsGlobal',
        commonjs: '@scope/components',
        commonjs2: '@scope/components',
        amd: 'components',
      });
    },
  ],
};
```

### 正则表达式

符合给定正则表达式的每个依赖项都将从打包产物中排除。

```js title="rspack.config.mjs"
export default {
  //...
  externals: /^(jquery|\$)$/i,
};
```

在这种情况下，任何名为 `jQuery` 的依赖项，无论是否大写，或者 `$`，都将被外部化。

### 复合语法

有时你可能希望使用上述语法的组合。这可以通过以下方式完成：

```js title="rspack.config.mjs"
export default {
  //...
  externals: [
    {
      // 字符串
      react: 'react',
      // 对象
      lodash: {
        commonjs: 'lodash',
        amd: 'lodash',
        root: '_', // indicates global variable
      },
      // 字符串数组
      subtract: ['./math', 'subtract'],
    },
    // 函数
    function ({ context, request }, callback) {
      if (/^yourregex$/.test(request)) {
        return callback(null, 'commonjs ' + request);
      }
      callback();
    },
    // 正则表达式
    /^(jquery|\$)$/i,
  ],
};
```

:::warning
如果没有指定类型，将使用[默认类型](#externalstype)，例如 `externals: { react: 'react' }` 而不是 `externals: { react: 'commonjs-module react' }`。
:::

## externalsType

- **类型：** `string`
- **默认值：** `'var'`

指定 externals 的默认类型。当 external 被设置为 `amd`，`umd`，`system` 以及 `jsonp` 时，[`output.libraryTarget`](/config/output#outputlibrarytarget) 的值也应相同。例如，你只能在 `amd` 库中使用 `amd` 的 externals。

支持的类型如下：

- `'amd'`
- `'amd-require'`
- `'assign'` - 同 `'var'`
- [`'commonjs'`](#externalstypecommonjs)
- `'commonjs-module'`
- [`'global'`](#externalstypeglobal)
- [`'module'`](#externalstypemodule)
- [`'import'`](#externalstypeimport) - 使用 `import()` 加载一个原生的 ECMAScript 模块（异步模块）
- [`'module-import'`](#externalstypemodule-import)
- [`'commonjs-import'`](#externalstypecommonjs-import)
- `'jsonp'`
- [`'node-commonjs'`](#externalstypenode-commonjs)
- [`'promise'`](#externalstypepromise)
- [`'self'`](#externalstypeself)
- `'system'`
- [`'script'`](#externalstypescript)
- [`'this'`](#externalstypethis)
- `'umd'`
- `'umd2'`
- [`'var'`](#externalstypevar)
- [`'window'`](#externalstypewindow)

```js title="rspack.config.mjs"
export default {
  //...
  externalsType: 'promise',
};
```

### externalsType.commonjs

将 externals 的默认类型指定为 `'commonjs'`。Rspack 将为模块中使用的外部生成类似 `const X = require('...')` 的代码。

**示例**

```js
import fs from 'fs-extra';
```

```js title="rspack.config.mjs"
export default {
  // ...
  externalsType: 'commonjs',
  externals: {
    'fs-extra': 'fs-extra',
  },
};
```

将会转换为类似下面的代码：

```js
const fs = require('fs-extra');
```

请注意，输出产物中会有一个 `require()`。

### externalsType.global

将 externals 的默认类型指定为 `'global'`。Rspack 会将 external 视为 [`globalObject`](/config/output#outputglobalobject) 上的一个全局变量读取。

**示例**

```js
import jq from 'jquery';
jq('.my-element').animate(/* ... */);
```

```js title="rspack.config.mjs"
export default {
  // ...
  externalsType: 'global',
  externals: {
    jquery: '$',
  },
  output: {
    globalObject: 'global',
  },
};
```

将会转换为类似下面的代码：

```js
const jq = global['$'];
jq('.my-element').animate(/* ... */);
```

### externalsType.module

将 externals 的默认类型指定为 `'module'`。Rspack 将为模块中使用的 externals 生成类似 `import * as X from '...'` 的代码。

确保开启了 [`experiments.outputModule`](/config/experiments#experimentsoutputmodule)，否则 Rspack 会抛出错误。

**示例**

```js
import jq from 'jquery';
jq('.my-element').animate(/* ... */);
```

```js title="rspack.config.mjs"
export default {
  experiments: {
    outputModule: true,
  },
  externalsType: 'module',
  externals: {
    jquery: 'jquery',
  },
};
```

将会转换为类似下面的代码：

```js
import * as __rspack_external_jquery from 'jquery';

const jq = __rspack_external_jquery['default'];
jq('.my-element').animate(/* ... */);
```

请注意，在输出产物中将有 `import` 语句。

### externalsType.import

将 externals 的默认类型指定为 `'import'`。Rspack 将为模块中使用的 externals 生成类似 `import('...')` 的代码。

**示例**

```js
async function foo() {
  const jq = await import('jquery');
  jq('.my-element').animate(/* ... */);
}
```

```js title="rspack.config.mjs"
export default {
  externalsType: 'import',
  externals: {
    jquery: 'jquery',
  },
};
```

将会转换为类似下面的代码：

```js
var __webpack_modules__ = {
  jquery: module => {
    module.exports = import('jquery');
  },
};

// webpack runtime...

async function foo() {
  const jq = await Promise.resolve(/* import() */).then(
    __webpack_require__.bind(__webpack_require__, 'jquery'),
  );
  jq('.my-element').animate(/* ... */);
}
```

请注意，在输出产物中将有 `import()` 语句。

### externalsType['module-import']

将 externals 的默认类型指定为 `'module-import'`。这将结合 [`'module'`](#externalstypemodule) 和 [`'import'`](#externalstypeimport)。Rspack 将自动检测导入语法的类型，对于静态导入设置为 `'module'`，对于动态导入设置为 `'import'`。

**示例**

```js
import { attempt } from 'lodash';

async function foo() {
  const jq = await import('jquery');
  attempt(() => jq('.my-element').animate(/* ... */));
}
```

```js title="rspack.config.mjs"
export default {
  externalsType: 'module-import',
  externals: {
    jquery: 'jquery',
  },
};
```

将会转换为类似下面的代码：

```js
import * as __rspack_external_lodash from 'lodash';
const lodash = __rspack_external_jquery;

var __webpack_modules__ = {
  jquery: module => {
    module.exports = import('jquery');
  },
};

// webpack runtime...

async function foo() {
  const jq = await Promise.resolve(/* import() */).then(
    __webpack_require__.bind(__webpack_require__, 'jquery'),
  );
  (0, lodash.attempt)(() => jq('.my-element').animate(/* ... */));
}
```

请注意，在输出产物中将有 `import` 或 `import()` 语句。

当一个模块没有通过 `import` 或 `import()` 导入时，Rspack 将使用 `"module"` externals type 作为回退。如果你想使用不同类型的 externals 作为回退，你可以在 `externals` 选项中指定一个函数。例如：

```js title="rspack.config.mjs"
export default {
  externalsType: "module-import",
  externals: [
    function (
      { request, dependencyType },
      callback
    ) {
      if (dependencyType === "commonjs") {
        return callback(null, `node-commonjs ${request}`);
      }
      callback();
    },
  ]
```

### externalsType['commonjs-import']

将 externals 的默认类型指定为 `'commonjs-import'`。这将结合 [`'commonjs'`](#externalstypecommonjs) 和 [`'import'`](#externalstypeimport)。Rspack 将自动检测导入语法的类型，对于动态导入设置为 `'import'`，其他的导入设置为 `'commonjs'`。

这在构建一个 Node.js 应用程序时非常有用，当目标 Node.js 版本高于 `13.2.0`，同时支持 [`import()` 表达式](https://nodejs.org/api/esm.html#import-expressions) 和 `require()`。

:::note
`commonjs-import` 类型仅在 Rspack 中可用，webpack 并不支持此类型。
:::

**示例**

```js
import { attempt } from 'lodash';

async function foo() {
  const jq = await import('jquery');
  attempt(() => jq('.my-element').animate(/* ... */));
}
```

```js title="rspack.config.mjs"
export default {
  externalsType: 'commonjs-import',
  externals: {
    lodash: 'lodash',
    jquery: 'jquery',
  },
};
```

将会转换为类似下面的代码：

```js
var __webpack_modules__ = {
  lodash: function (module) {
    module.exports = require('lodash');
  },
  jquery: function (module) {
    module.exports = import('jquery');
  },
};

// Rspack runtime...

async function foo() {
  const jq = await Promise.resolve(/* import() */).then(
    __webpack_require__.bind(__webpack_require__, 'jquery'),
  );
  (0, lodash__rspack_import_0__.attempt)(() =>
    jq('.my-element').animate(/* ... */),
  );
}
```

请注意，在输出产物中将有 `import()` 语句。

### externalsType['node-commonjs']

将 externals 类型设置为 `'node-commonjs'`，Rspack 将从 `module` 中导入 [`createRequire`](https://nodejs.org/api/module.html#module_module_createrequire_filename) 来构造一个 require 函数，用于加载模块中使用的外部对象。

**示例**

```js
import jq from 'jquery';
jq('.my-element').animate(/* ... */);
```

```js title="rspack.config.mjs"
export default {
  externalsType: 'node-commonjs',
  externals: {
    jquery: 'jquery',
  },
};
```

将会转换为类似下面的代码：

```js
import { createRequire } from 'module';

const jq = createRequire(import.meta.url)('jquery');
jq('.my-element').animate(/* ... */);
```

请注意，在输出产物中会有 `import` 语句。

### externalsType.promise

将 externals 的默认类型指定为 `'promise'`，Rspack 会将 external 视为全局变量（类似于 [`'var'`](#externalstypepromise)）并 await 它。

**例子**

```js
import jq from 'jquery';
jq('.my-element').animate(/* ... */);
```

```js title="rspack.config.mjs"
export default {
  // ...
  externalsType: 'promise',
  externals: {
    jquery: '$',
  },
};
```

将会转换为类似下面的代码：

```js
const jq = await $;
jq('.my-element').animate(/* ... */);
```

### externalsType.self

将外部的默认类型指定为 `'self'`。 Rspack 会将 external 作为 `self` 对象上的全局变量读取。

```js
import jq from 'jquery';
jq('.my-element').animate(/* ... */);
```

```js title="rspack.config.mjs"
export default {
  // ...
  externalsType: 'self',
  externals: {
    jquery: '$',
  },
};
```

将会转换为类似下面的代码：

```js
const jq = self['$'];
jq('.my-element').animate(/* ... */);
```

### externalsType.script

将 external 的默认类型指定为 `'script'`。Rspack 会使用 HTML `<script>` 标签加载外部资源，暴露预定义的全局变量。脚本加载完成后，`<script>` 标签将被移除。

**语法**

```js title="rspack.config.mjs"
export default {
  externalsType: 'script',
  externals: {
    packageName: [
      'http://example.com/script.js',
      'global',
      'property',
      'property',
    ], // 属性是可选的
  },
};
```

如果不打算指定任何属性也可以使用快捷语法：

```js title="rspack.config.mjs"
export default {
  externalsType: 'script',
  externals: {
    packageName: 'global@http://example.com/script.js', // 这里没有属性
  },
};
```

请注意，[output.publicPath](/config/output#outputpublicpath) 不会被添加到提供的 URL 中。

**示例**

让我们从 CDN 加载一个 `lodash`：

```js title="rspack.config.mjs"
export default {
  // ...
  externalsType: 'script',
  externals: {
    lodash: ['https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js', '_'],
  },
};
```

然后在代码中使用：

```js
import _ from 'lodash';
console.log(_.head([1, 2, 3]));
```

下面是我们如何为上述示例指定属性的方法：

```js title="rspack.config.mjs"
export default {
  // ...
  externalsType: 'script',
  externals: {
    lodash: [
      'https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js',
      '_',
      'head',
    ],
  },
};
```

局部变量 `head` 和全局 `window._` 在导入 `lodash` 时都将被暴露出来。

```js
import head from 'lodash';
console.log(head([1, 2, 3])); // logs 1 here
console.log(window._.head(['a', 'b'])); // logs a here
```

:::tip
当使用 HTML `<script>` 标签加载代码时，Rspack 运行时会尝试查找与 `src` 属性匹配或具有特定 `data-webpack` 属性的现有 `<script>` 标签。对于 chunk 加载，`data-webpack` 属性的值将是 `'[output.uniqueName]:chunk-[chunkId]'`，而外部脚本的值将是 `'[output.uniqueName]:[global]'`。
:::

### externalsType.this

将 external 的默认类型指定为 `'this'`。Rspack 会将 external 作为 `this` 对象上的全局变量读取。

**示例**

```js
import jq from 'jquery';
jq('.my-element').animate(/* ... */);
```

```js title="rspack.config.mjs"
export default {
  // ...
  externalsType: 'this',
  externals: {
    jquery: '$',
  },
};
```

将会转换为类似下面的代码：

```js
const jq = this['$'];
jq('.my-element').animate(/* ... */);
```

### externalsType.var

将 external 的默认类型指定为 `'var'`。Rspack 会将 external 作为全局变量读取。

**示例**

```js
import jq from 'jquery';
jq('.my-element').animate(/* ... */);
```

```js title="rspack.config.mjs"
export default {
  // ...
  externalsType: 'var',
  externals: {
    jquery: '$',
  },
};
```

将会转换为类似下面的代码：

```js
const jq = $;
jq('.my-element').animate(/* ... */);
```

### externalsType.window

将 external 的默认类型指定为 `'window'`。Rspack 会将 external 作为 `window` 对象上的全局变量读取。

**示例**

```js
import jq from 'jquery';
jq('.my-element').animate(/* ... */);
```

```js title="rspack.config.mjs"
export default {
  // ...
  externalsType: 'window',
  externals: {
    jquery: '$',
  },
};
```

将会转换为类似下面的代码：

```js
const jq = window['$'];
jq('.my-element').animate(/* ... */);
```

## externalsPresets

- **类型：** `object`

为特定的目标环境启用外部模块的预设值。

### externalsPresets.electron

**类型：**`boolean`

将 Electron 主进程和预加载脚本中常见的 Electron 内置模块如 `electron`、`ipc` 或 `shell` 视为外部模块，并在使用时通过 `require()` 加载它们。

### externalsPresets.electronMain

**类型：**`boolean`

将 Electron 主进程中常见的 Electron 内置模块如 `app`、`ipc-main` 或 `shell` 视为外部模块，并在使用时通过 `require()` 加载它们。

### externalsPresets.electronPreload

**类型：**`boolean`

将 Electron 预加载脚本中常见的 Electron 内置模块如 `web-frame`、`ipc-renderer` 或 `shell` 视为外部模块，并在使用时通过 `require()` 加载它们。

### externalsPresets.electronRenderer

**类型：**`boolean`

将 Electron 渲染进程中常见的 Electron 内置模块如 `web-frame`、`ipc-renderer` 或 `shell` 视为外部模块，并在使用时通过 `require()` 加载它们。

### externalsPresets.node

**类型：**`boolean`

将 node.js 的内置模块（如 `fs`、`path` 或 `vm`）视为外部模块，并在使用时通过 `require()` 加载它们。

### externalsPresets.nwjs

**类型：**`boolean`

将 NW.js 旧版 `nw.gui` 模块视为外部模块，并在使用时通过 `require()` 加载它。

### externalsPresets.web

**类型：**`boolean`

将对 `http(s)://...` 和 `std:...` 引入的模块视为外部模块，并在使用时通过 `import` (`externalType: "module"`) 来加载它们 **（请注意，这会改变执行顺序，因为外部模块的代码会在 chunk 中的任何其他模块的代码之前执行）**。

### externalsPresets.webAsync

**类型：**`boolean`

将对 `http(s)://...` 和 `std:...` 引入的模块视为外部模块，并在使用时通过 `async import()` 来加载它们 **（注意，外部模块的类型是 `async` 模块，对执行有各种影响）**。

请注意，如果你打算输出 ES 模块并使用这些 node.js 相关的预设值，Rspack 将会将默认的 `externalsType` 设置为 [`node-commonjs`](#externalstypenode-commonjs)，这将使用 `createRequire` 来构造一个 require 函数，而不是使用 `require()`。

**示例**

使用 `node` 预设将不会打包内置模块，并将它们视为外部模块，当使用时通过 `require()` 加载它们。

```js title="rspack.config.mjs"
export default {
  // ...
  externalsPresets: {
    node: true,
  },
};
```
